//! # OAuth2 认证提供者实现
//!
//! 基于 OAuth2 协议的认证实现，支持多种 OAuth2 流程和提供商。

use std::collections::HashMap;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use rustcloud_security::prelude::*;

/// OAuth2 认证配置
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OAuth2Config {
    /// 客户端ID
    pub client_id: String,
    /// 客户端密钥
    pub client_secret: String,
    /// 授权端点
    pub authorization_endpoint: String,
    /// 令牌端点
    pub token_endpoint: String,
    /// 用户信息端点
    pub userinfo_endpoint: Option<String>,
    /// 重定向URI
    pub redirect_uri: String,
    /// 作用域
    pub scopes: Vec<String>,
    /// OAuth2 提供商类型
    pub provider_type: OAuth2ProviderType,
    /// 自定义配置
    pub custom_config: HashMap<String, String>,
}

impl Default for OAuth2Config {
    fn default() -> Self {
        Self {
            client_id: "".to_string(),
            client_secret: "".to_string(),
            authorization_endpoint: "".to_string(),
            token_endpoint: "".to_string(),
            userinfo_endpoint: None,
            redirect_uri: "http://localhost:8080/auth/callback".to_string(),
            scopes: vec!["openid".to_string(), "profile".to_string(), "email".to_string()],
            provider_type: OAuth2ProviderType::Generic,
            custom_config: HashMap::new(),
        }
    }
}

/// OAuth2 提供商类型
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OAuth2ProviderType {
    /// 通用 OAuth2
    Generic,
    /// Google
    Google,
    /// GitHub
    GitHub,
    /// Microsoft Azure AD
    AzureAd,
    /// 自定义
    Custom(String),
}

/// OAuth2 令牌响应
#[derive(Debug, Serialize, Deserialize)]
pub struct TokenResponse {
    pub access_token: String,
    pub token_type: String,
    pub expires_in: Option<u64>,
    pub refresh_token: Option<String>,
    pub scope: Option<String>,
    pub id_token: Option<String>,
}

/// OAuth2 用户信息
#[derive(Debug, Serialize, Deserialize)]
pub struct UserInfo {
    pub sub: String,
    pub name: Option<String>,
    pub email: Option<String>,
    pub picture: Option<String>,
    pub locale: Option<String>,
    #[serde(flatten)]
    pub additional: HashMap<String, serde_json::Value>,
}

/// OAuth2 认证提供者
pub struct OAuth2Provider {
    config: OAuth2Config,
    http_client: reqwest::Client,
}

impl OAuth2Provider {
    /// 创建新的 OAuth2 提供者
    pub fn new(config: OAuth2Config) -> AuthResult<Self> {
        let http_client = reqwest::Client::new();
        
        Ok(Self {
            config,
            http_client,
        })
    }

    /// 生成授权URL
    pub fn get_authorization_url(&self, state: &str) -> String {
        let mut url = format!(
            "{}?client_id={}&redirect_uri={}&response_type=code&state={}",
            self.config.authorization_endpoint,
            urlencoding::encode(&self.config.client_id),
            urlencoding::encode(&self.config.redirect_uri),
            urlencoding::encode(state)
        );

        if !self.config.scopes.is_empty() {
            url.push_str(&format!("&scope={}", urlencoding::encode(&self.config.scopes.join(" "))));
        }

        url
    }

    /// 使用授权码交换访问令牌
    pub async fn exchange_code_for_token(&self, code: &str) -> AuthResult<TokenResponse> {
        let mut params = HashMap::new();
        params.insert("grant_type", "authorization_code");
        params.insert("client_id", &self.config.client_id);
        params.insert("client_secret", &self.config.client_secret);
        params.insert("code", code);
        params.insert("redirect_uri", &self.config.redirect_uri);

        let response = self.http_client
            .post(&self.config.token_endpoint)
            .form(&params)
            .send()
            .await
            .map_err(|e| AuthError::ExternalServiceError(format!("Failed to exchange code: {}", e)))?;

        if !response.status().is_success() {
            let error_text = response.text().await.unwrap_or_default();
            return Err(AuthError::ExternalServiceError(format!("Token exchange failed: {}", error_text)));
        }

        let token_response: TokenResponse = response.json().await
            .map_err(|e| AuthError::ExternalServiceError(format!("Failed to parse token response: {}", e)))?;

        Ok(token_response)
    }

    /// 获取用户信息
    pub async fn get_user_info(&self, access_token: &str) -> AuthResult<UserInfo> {
        let userinfo_endpoint = self.config.userinfo_endpoint.as_ref()
            .ok_or_else(|| AuthError::ConfigurationError("UserInfo endpoint not configured".to_string()))?;

        let response = self.http_client
            .get(userinfo_endpoint)
            .bearer_auth(access_token)
            .send()
            .await
            .map_err(|e| AuthError::ExternalServiceError(format!("Failed to get user info: {}", e)))?;

        if !response.status().is_success() {
            let error_text = response.text().await.unwrap_or_default();
            return Err(AuthError::ExternalServiceError(format!("UserInfo request failed: {}", error_text)));
        }

        let user_info: UserInfo = response.json().await
            .map_err(|e| AuthError::ExternalServiceError(format!("Failed to parse user info: {}", e)))?;

        Ok(user_info)
    }

    /// 从用户信息创建 Principal
    fn user_info_to_principal(&self, user_info: &UserInfo, token_response: &TokenResponse) -> Principal {
        let user_id = user_info.sub.clone();
        let username = user_info.name.clone().unwrap_or_else(|| user_id.clone());
        
        let mut principal = Principal::new(
            user_id,
            username,
            PrincipalType::User,
        );

        // 添加邮箱信息
        if let Some(ref email) = user_info.email {
            principal = principal.add_attribute("email".to_string(), email.clone());
        }

        // 添加头像信息
        if let Some(ref picture) = user_info.picture {
            principal = principal.add_attribute("picture".to_string(), picture.clone());
        }

        // 添加语言信息
        if let Some(ref locale) = user_info.locale {
            principal = principal.add_attribute("locale".to_string(), locale.clone());
        }

        // 添加OAuth2特定属性
        principal = principal.add_attribute("oauth2_provider".to_string(), format!("{:?}", self.config.provider_type));
        if let Some(ref token_type) = Some(&token_response.token_type) {
            principal = principal.add_attribute("token_type".to_string(), token_type.clone());
        }

        // 设置过期时间
        if let Some(expires_in) = token_response.expires_in {
            let expiry = std::time::SystemTime::now() + std::time::Duration::from_secs(expires_in);
            principal = principal.with_expiry(Some(expiry));
        }

        // 添加额外属性
        for (key, value) in &user_info.additional {
            if let Ok(string_value) = serde_json::from_value::<String>(value.clone()) {
                principal = principal.add_attribute(format!("oauth2_{}", key), string_value);
            }
        }

        principal
    }
}

#[async_trait]
impl AuthProvider for OAuth2Provider {
    async fn authenticate(&self, context: &AuthContext) -> AuthResult<Principal> {
        // OAuth2 通常不直接从请求中认证，而是通过授权码流程
        // 这里提供一个简化的实现，假设授权码在查询参数中
        
        // 从查询参数中提取授权码
        let code = context.query_params.as_ref()
            .and_then(|params| params.get("code"))
            .ok_or_else(|| AuthError::MissingCredentials("Authorization code not found".to_string()))?;

        // 交换访问令牌
        let token_response = self.exchange_code_for_token(code).await?;

        // 获取用户信息
        let user_info = self.get_user_info(&token_response.access_token).await?;

        // 创建 Principal
        let principal = self.user_info_to_principal(&user_info, &token_response);

        Ok(principal)
    }

    async fn validate_token(&self, token: &str) -> AuthResult<bool> {
        // 尝试使用访问令牌获取用户信息来验证
        match self.get_user_info(token).await {
            Ok(_) => Ok(true),
            Err(_) => Ok(false),
        }
    }

    async fn refresh_token(&self, refresh_token: &str) -> AuthResult<String> {
        let mut params = HashMap::new();
        params.insert("grant_type", "refresh_token");
        params.insert("client_id", &self.config.client_id);
        params.insert("client_secret", &self.config.client_secret);
        params.insert("refresh_token", refresh_token);

        let response = self.http_client
            .post(&self.config.token_endpoint)
            .form(&params)
            .send()
            .await
            .map_err(|e| AuthError::ExternalServiceError(format!("Failed to refresh token: {}", e)))?;

        if !response.status().is_success() {
            let error_text = response.text().await.unwrap_or_default();
            return Err(AuthError::ExternalServiceError(format!("Token refresh failed: {}", error_text)));
        }

        let token_response: TokenResponse = response.json().await
            .map_err(|e| AuthError::ExternalServiceError(format!("Failed to parse refresh response: {}", e)))?;

        Ok(token_response.access_token)
    }

    async fn logout(&self, _principal: &Principal) -> AuthResult<()> {
        // OAuth2 注销通常需要撤销令牌，这里提供基本实现
        // 具体实现取决于提供商的支持
        Ok(())
    }

    async fn get_user_info(&self, principal: &Principal) -> AuthResult<serde_json::Value> {
        Ok(serde_json::json!({
            "id": principal.id,
            "name": principal.name,
            "type": principal.principal_type,
            "attributes": principal.attributes,
            "provider": "oauth2"
        }))
    }

    fn supports_token_refresh(&self) -> bool {
        true
    }

    fn provider_type(&self) -> AuthProviderType {
        AuthProviderType::OAuth2
    }

    fn provider_name(&self) -> &str {
        "oauth2"
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_oauth2_config_default() {
        let config = OAuth2Config::default();
        assert!(config.scopes.contains(&"openid".to_string()));
        assert_eq!(config.provider_type, OAuth2ProviderType::Generic);
    }

    #[test]
    fn test_oauth2_provider_creation() {
        let config = OAuth2Config::default();
        let provider = OAuth2Provider::new(config);
        assert!(provider.is_ok());
    }

    #[test]
    fn test_authorization_url_generation() {
        let mut config = OAuth2Config::default();
        config.client_id = "test_client".to_string();
        config.authorization_endpoint = "https://example.com/oauth/authorize".to_string();
        config.redirect_uri = "http://localhost:8080/callback".to_string();
        
        let provider = OAuth2Provider::new(config).unwrap();
        let auth_url = provider.get_authorization_url("test_state");
        
        assert!(auth_url.contains("client_id=test_client"));
        assert!(auth_url.contains("state=test_state"));
        assert!(auth_url.contains("response_type=code"));
    }
}